#include <stdio.h>  /* BUFSIZ 8192 */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <pwd.h>
#include <dirent.h>
#include <ncurses/curses.h>
#include <ncurses/term.h>
#include <termios.h>
#include <langinfo.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <locale.h>

// /usr/include/stdio.h:99:#define BUFSIZ 8192

/*
 * ncurses
 * https://ftp.gnu.org/pub/gnu/ncurses/
 * 
 *  ./configure CC=aarch64-v01c01-linux-gnu-gcc --build=aarch64 --host=aarch64-v01c01-linux-gnu --disable-stripping --without-manpages --without-tack --without-tests --with-termlib --enable-termcap -prefix=/home/share/work/r/ncurses-6.3/install --exec-prefix=/home/share/work/r/ncurses-6.3/install
 * 
 * psmisc
 * https://gitlab.com/psmisc/psmisc
 * git : 3098e64 - (HEAD -> master, tag: v23.6)
 * ./configure CC=aarch64-v01c01-linux-gnu-gcc --build=aarch64 --host=aarch64-v01c01-linux-gnu  LDFLAGS="-L/home/share/work/r/ncurses-6.3/install/lib" CFLAGS="-I/home/share/work/r/ncurses-6.3/install/include" --disable-option-checking --disable-nls --prefix=/home/share/work/r/psmisc/install/ --exec-prefix=/home/share/work/r/psmisc/install/
 */

/*
 * 小药仙
 *
 * https://m.alltuu.com/album/1415013713/?menu=live
 */

#define UTF_V        "\342\224\202"        /* U+2502, Vertical line drawing char */
#define UTF_VR        "\342\224\234"        /* U+251C, Vertical and right */
#define UTF_H        "\342\224\200"        /* U+2500, Horizontal */
#define UTF_UR        "\342\224\224"        /* U+2514, Up and right */
#define UTF_HD        "\342\224\254"        /* U+252C, Horizontal and down */

#define VT_BEG        "\033(0\017"        /* use graphic chars */
#define VT_END        "\033(B"        /* back to normal char set */
#define VT_V        "x"                /* see UTF definitions above */
#define VT_VR        "t"
#define VT_H        "q"
#define VT_UR        "m"
#define        VT_HD        "w"

#define THREAD_FORMAT        "{%.*s}"        /* Format for thread names */
#define COMM_LEN 64

enum ns_type {
    CGROUPNS = 0,
    IPCNS,
    MNTNS,
    NETNS,
    PIDNS,
    USERNS,
    UTSNS,
    TIMENS,
    NUM_NS
};

enum color_type {
    COLOR_NONE = 0,
    COLOR_AGE,
    NUM_COLOUR
};

static const char *ns_names[] = {
    [CGROUPNS] = "cgroup",
    [IPCNS] = "ipc",
    [MNTNS] = "mnt",
    [NETNS] = "net",
    [PIDNS] = "pid",
    [USERNS] = "user",
    [UTSNS] = "uts",
    [TIMENS] = "time",
};

typedef struct _proc {
    char comm[COMM_LEN + 2 + 1]; /* add another 2 for thread brackets */    // 72           // ==> 0x00     00
    char **argv;    // 8         /* only used : argv[0] is 1st arg; undef if argc < 1 */    // ==> 0x48     72
    int argc;       // 4         /* with -a   : number of arguments, -1 if swapped    */    // ==> 0x50     80
    pid_t pid;      // 4                // ==> 0x54     84
    pid_t pgid;     // 4                // ==> 0x58     88
    pid_t uid;      // 4                // ==> 0x5c     92
    ino_t ns[8];    // 64               // ==> 0x60     96
    char flags;     // 8                // ==> 0xA0     160
    double age;     // 8                // ==> 0xA8     168
    struct _child *children;    // 8    // ==> 0xB0     176
    struct _proc *parent;       // 8    // ==> 0xB8     184
    struct _proc *next;         // 8    // ==> 0xC0     192
} PROC;

typedef struct _child {
    PROC *child;                // ==> 0x00
    struct _child *next;        // ==> 0x08
} CHILD;

struct ns_entry {
    ino_t number;               // ==> 0x00
    CHILD *children;            // ==> 0x08
    struct ns_entry *next;      // ==> 0x10
};

/* For flags above */
#define PFLAG_HILIGHT   0x01
#define PFLAG_THREAD    0x02

static struct {
    const char *empty_2;        /*    */    /* 0x00 */
    const char *branch_2;       /* |- */    /* 0x08 */
    const char *vert_2;         /* |  */    /* 0x10 */
    const char *last_2;         /* `- */    /* 0x18 */
    const char *single_3;       /* --- */   /* 0x20 */
    const char *first_3;        /* -+- */   /* 0x28 */
} sym_ascii = {
"  ", "|-", "| ", "`-", "---", "-+-"}

, sym_utf = {
"  ",
        UTF_VR UTF_H,
        UTF_V " ",
        UTF_UR UTF_H, UTF_H UTF_H UTF_H, UTF_H UTF_HD UTF_H}, sym_vt100 = {
"  ",
        VT_BEG VT_VR VT_H VT_END,
        VT_BEG VT_V VT_END " ",
        VT_BEG VT_UR VT_H VT_END,
        VT_BEG VT_H VT_H VT_H VT_END, VT_BEG VT_H VT_HD VT_H VT_END}

, *sym = &sym_ascii;

static PROC *list = NULL;

struct age_to_color {
    unsigned int age_seconds;
    char *color;
};

struct age_to_color age_to_color[] = {
    { 60, "\033[32m"},
    {3600,   "\033[33m"},
    {0,	    "\033[31m"}
    };

/* The buffers will be dynamically increased in size as needed. */
static int capacity = 0;
static int *width = NULL;
static int *more = NULL;

static int print_args = 0, compact = 1, user_change = 0, pids = 0, pgids = 0,
    show_parents = 0, by_pid = 0, trunc = 1, wait_end = 0, ns_change = 0,
    thread_names = 0, hide_threads = 0;
static int show_scontext = 0;
static int output_width = 132;
static int cur_x = 1;
static char last_char = 0;
static int dumped = 0;                /* used by dump_by_user */
static int charlen = 0;                /* length of character */
static enum color_type color_highlight = COLOR_NONE;

static void usage(void);

const char *get_ns_name(enum ns_type id) {
    if (id >= NUM_NS)
        return NULL;
    return ns_names[id];
}

static void add_child(PROC * parent, PROC * child)
{
    CHILD *new, **walk;
    int cmp;
    
    // 403110
    if (!(new = malloc(sizeof(CHILD)))) {
        perror("malloc");
        exit(1);
    }
    // 40311c
    new->child = child;
    for (walk = &parent->children; *walk; walk = &(*walk)->next)
        if (by_pid) {
            if ((*walk)->child->pid > child->pid)
                break;
        } else if ((cmp = strcmp((*walk)->child->comm, child->comm)) > 0) {
            break; }
        else if (!cmp && (*walk)->child->uid > child->uid)
            break;
    new->next = *walk;
    *walk = new;
}

static inline void new_proc_ns(PROC *ns_task)
{
    struct stat st;
    char buff[50];
    pid_t pid = ns_task->pid;       // 被优化去掉，用w21存储pid
    int i;

    // 403610
    for (i = 0; i < NUM_NS; i++) {
        snprintf(buff, sizeof(buff), "/proc/%i/ns/%s", pid,
                 get_ns_name(i));
        if (stat(buff, &st)) {  // 成功则返回0,失败返回-1，错误代码存于errno
            ns_task->ns[i] = 0; // 失败返回-1，执行if 
            continue;
        }   // 用else代替，就可以不用continue语句
        ns_task->ns[i] = st.st_ino; // 成功
    }
}

static PROC *new_proc(const char *comm, pid_t pid, uid_t uid)
{
    PROC *new;

    // 4035b0
    if (!(new = malloc(sizeof(PROC)))) {
        perror("malloc");
        exit(1);
    }

    strncpy(new->comm, comm, COMM_LEN+2);
    new->comm[COMM_LEN+1] = '\0';     /* make sure nul terminated*/
    new->pid = pid;
    new->uid = uid;
    new->flags = 0;
    new->argc = 0;
    new->argv = NULL;
    new->children = NULL;
    new->parent = NULL;
    new->next = list;
    new_proc_ns(new);       // 403610
    return list = new;
}

static void
rename_proc(PROC *this, const char *comm, uid_t uid)
{
    PROC *tmp_child, *parent;
    CHILD **walk;
    
    // 403718
    strncpy(this->comm, comm, COMM_LEN+2);
    this->comm[COMM_LEN+1] = '\0';
    this->uid = uid;

    /* Re-sort children in parent, now we have a name */
    // 40373c
    if (!by_pid && this->parent) {
        parent = this->parent;
        for (walk = &parent->children; *walk; walk = &(*walk)->next) {
            if (
                ((*walk)->next != NULL) &&
                strcmp((*walk)->child->comm, (*walk)->next->child->comm) > 0 ) {
                tmp_child = (*walk)->child;
                (*walk)->child = (*walk)->next->child;
                (*walk)->next->child = tmp_child;
            }
        }
    }
}

static void set_args(PROC * this, const char *args, int size)
{
    char *start;
    int i;

    // 403784
    if (!size) {
        // 40393c
        this->argc = -1;
        /*this->pgid = pgid;
        if (pid == ppid)
            ppid = 0;
        if (isthread == 0) {
            // cbz 4038a4
        }
        b 4038e8 */
        return; // 封装成函数时是return，汇编中为以上指令流程顺序执行；
    }
    this->argc = 0;
    for (i = 0; i < size - 1; i++)
        if (!args[i]) {
            this->argc++;
            /* now skip consecutive NUL */
            // 4037d8
            while(!args[i] && (i < size -1 ))
                i++;
        }
    if (!this->argc)
        return;
    if (!(this->argv = malloc(sizeof(char *) * this->argc))) {
        perror("malloc");
        exit(1);
    }
    start = strchr(args, 0) + 1;        // -> strlen(args)
    size -= start - args;
    if (!(this->argv[0] = malloc((size_t) size))) {
        perror("malloc");
        exit(1);
    }
    start = memcpy(this->argv[0], start, (size_t) size);
    // 403858
    for (i = 1; i < this->argc; i++)
        this->argv[i] = start = strchr(start, 0) + 1;
}

static void
add_proc(const char *comm, pid_t pid, pid_t ppid, pid_t pgid, uid_t uid,
         const char *args, int size, char isthread, double process_age_sec)
{
    PROC *this, *parent;

    if (!(this = find_proc(pid)))
        // 403928
        this = new_proc(comm, pid, uid);
        // b 403780
    else {
        // 403718
        rename_proc(this, comm, uid);
    }
    // 403780
    if (args)
        // 403784
        set_args(this, args, size);
    // 403888
    if (pid == ppid)    // 注意以下五行代码有两处。
        ppid = 0;
    this->pgid = pgid;
    this->age = process_age_sec;
    if (isthread) {
      // 4038e8
      this->flags |= PFLAG_THREAD;
      // b 4038a4
    }
    // 4038a4
    if (!(parent = find_proc(ppid))) {
        // 403964
        parent = new_proc("?", ppid, 0);
        // 4038c4
    }
    // 4038c4
    if (pid != 0) {
      // cbnz 4038f8
      add_child(parent, this);
      this->parent = parent;
    }
}

static int out_int(int x)
{                                /* non-negative integers only */
    int digits, div;

    // 403d48
    digits = 0;
    for (div = 1; x / div; div *= 10)
        digits++;
    if (!digits)
        digits = 1;
    // 403d64
    for (div /= 10; div; div /= 10)
        out_char('0' + (x / div) % 10);
    return digits;
}

static void print_proc_color(const int process_age)
{
    struct age_to_color *p;
    switch(color_highlight) {
    case COLOR_AGE:
        // 403dc8
        for(p=age_to_color; p->age_seconds != 0; p++)
        if (process_age < p->age_seconds) break;

        // 403df0
        char *str = p->color;
        while (*str) putchar(*str++);
        break;
    default:
        break;
    }
}

static int
out_args(char *mystr)
{
  char *here;
  int strcount=0;
  char tmpstr[5];

  for (here = mystr; *here; here++) {
    // 4039f0
    if (*here == '\\') {
      out_string("\\\\");
      strcount += 2;
    // 4039f8
    } else if (*here >= ' ' && *here <= '~') {
      out_char(*here);
      strcount++;
    // 403a08
    } else {
      sprintf(tmpstr, "\\%03o", (unsigned char) *here);
      out_string(tmpstr);
      strcount += 4;
    }
  } /* for */
  return strcount;
}

static void out_scontext(const PROC *current)
{
    bool success = false;
    // 403ad0
    out_string("`");

#ifdef WITH_SELINUX
    success = out_selinux_context(current);
#endif /* WITH_SELINUX */

#ifdef WITH_APPARMOR
    success |= out_apparmor_context(current);
#endif /* WITH_APPARMOR */

    if (!success) {
        FILE *file;
        char path[50];
        char readbuf[BUFSIZ+1];
        int num_read;
        snprintf(path, sizeof path, "/proc/%d/attr/current", current->pid);
        // 403aec
        if ( (file = fopen(path, "r")) != NULL) {
            // 403b00
            if (fgets(readbuf, BUFSIZ, file) != NULL) {
                num_read = strlen(readbuf);
                readbuf[num_read-1] = '\0';
                out_string(readbuf);
            }
            fclose(file);
        }
    }
    out_string("'");
}

static void reset_color(void)
{
    // 4040d0
    if (color_highlight != COLOR_NONE) {
        char *str = "\033[0m";
        // 4040f0
        while (*str) putchar(*str++);
    }
}

static void out_newline(void)
{
    if (last_char && cur_x == output_width)
        // 40348c
        putchar(last_char);
    // 40346c
    last_char = 0;
    // 403470
    putchar('\n');
    cur_x = 1;
}

static void ensure_buffer_capacity(int index)
{
    // 4043b0 || 404124
    if (index >= capacity) {
        // 404128
        if (capacity == 0)
            // 404494
            capacity = 100;
        else
            capacity *= 2;
        // 404140
        if (!(width = realloc(width, capacity * sizeof(int)))) {
            perror("realloc");
            exit(1);
        }
        // 404150
        if (!(more = realloc(more, capacity * sizeof(int)))) {
            perror("realloc");
            exit(1);
        }
    }
    // 404168
}

static void
dump_tree(PROC * current, int level, int rep, int leaf, int last,
          uid_t prev_uid, int closing)
{
    // 403b80
    CHILD *walk, *next, *tmp_child, **scan;
    const struct passwd *pw;
    int lvl, i, add, offset, len, swapped, info, count, comm_len, first;
    const char *tmp, *here;

    assert(closing >= 0);
    // 403bd0
    if (!current)
        return; // 403d30
    // 403bd4
    if (!leaf)
        // 403e78
        for (lvl = 0; lvl < level; lvl++) {
            // 403e90
            for (i = width[lvl] + 1; i; i--)
                out_char(' ');
            // 403ed8
            out_string(lvl == level - 1 ? last ? sym->last_2 : sym->branch_2 : 
                       more[lvl + 1] ? sym->vert_2 : sym->empty_2);
        }

    // 403bdc
    if (rep < 2)
        add = 0;
    else {
        // 403d48
        add = out_int(rep) + 2;
        out_string("*[");
    }
    // (403bf8  --> 403dc8)     || 403dbc
    print_proc_color(current->age);
    // (403c04   --> 403e20)    || 403e18
    if ((current->flags & PFLAG_HILIGHT) && (tmp = tgetstr("md", NULL)))
        tputs(tmp, 1, putchar);
    // 403c0c || 403e44
    swapped = info = print_args;
    if (swapped && current->argc < 0)
        // 4045e0
        out_char('(');
    // 403c20 -> 403c4c || 403e50 -> 403c4c
    comm_len = out_args(current->comm);
    offset = cur_x;
    // 403c28 || 403e58
    if (pids) {
        // 403c48 || 403e74
        out_char(info++ ? ',' : '(');
        // 403c4c
        (void) out_int(current->pid);
    }
    // 403cac || 403f00
    if (pgids) {
        // 403cc4 || 403f10
        out_char(info++ ? ',' : '(');
        // 403f14
        (void) out_int(current->pgid);
    }
    // 403f74
    if (user_change && prev_uid != current->uid) {
        // 403fa0
        out_char(info++ ? ',' : '(');
        // 403fa8
        if ((pw = getpwuid(current->uid)))
            out_string(pw->pw_name);
        else
            (void) out_int(current->uid);
    }
    // 403fd0
    if (ns_change && current->parent) {
        // 403fe0
        for (i = 0; i < NUM_NS; i++) {
            // 403ff8
            if (current->ns[i] == 0 || current->parent->ns[i] == 0)
                continue;
            // 404018
            if (current->ns[i] != current->parent->ns[i]) {
                out_char(info++ ? ',' : '(');
                // 404030
                out_string(get_ns_name(i));
            }
        }
    }
    // 404058
    if (show_scontext) {
        out_char(info++ ? ',' : '(');
        // 40407c
        out_scontext(current);
    }
    if ((swapped && print_args && current->argc < 0) || (!swapped && info))
        // 4042a0
        out_char(')');
    // 40409c || 4042a8
    if ((current->flags & PFLAG_HILIGHT) && (tmp = tgetstr("me", NULL)))
        // 4040b8
        tputs(tmp, 1, putchar);
    // 4042b0
    if (print_args) {
        // 4042b8
        for (i = 0; i < current->argc; i++) {
            // 4042d0
            if (i < current->argc - 1)        /* Space between words but not at the end of last */
                out_char(' ');
            // 4042e0
            len = 0;
            for (here = current->argv[i]; *here; here++)
                len += *here >= ' ' && *here <= '~' ? 1 : 4;
            // 404314
            if (cur_x + len <=
                output_width - (i == current->argc - 1 ? 0 : 4) || !trunc)
              out_args(current->argv[i]);            
            else {
                // 4045c0  -> b 4040d0
                out_string("...");
                break;  // -> b 4040d0
            }
        }
    }
    // 4040d0
    reset_color();
    if (show_scontext || print_args || !current->children)
    {
        // 404388
        while (closing--)
            out_char(']');
        // 4043a0
        out_newline();
        // 4043a8
    }
    // 404118 || 4043a8
    ensure_buffer_capacity(level);
    // 404168
    more[level] = !last;

    // 404184
    if (show_scontext || print_args)
    {
        // 403cc8
        width[level] = swapped + (comm_len > 1 ? 0 : -1);
        count=0;
        first=1;
        // 403ce4
        for (walk = current->children; walk; walk = next) {
          next = walk->next;
          count=0;
          // 403cf4
          if (compact && (walk->child->flags & PFLAG_THREAD)) {
            // 4043f8
            scan = &walk->next;
            // 4043fc
            while (*scan) {
              // 40441c
              if (!tree_equal(walk->child, (*scan)->child)) {
                scan = &(*scan)->next;
              } else {
                // 404434
                if (next == *scan)
                  next = (*scan)->next;
                // 404444
                count++;
                tmp_child = (*scan)->next;
                free(*scan);
                *scan = tmp_child;
              }
            }
            // 40445c
            dump_tree(walk->child, level + 1, count + 1,
                  0, !next, current->uid, closing+ (count ? 2 : 1));
                 //closing + (count ? 1 : 0));
            // b 403d2c
          } else {
          // 403d00
          dump_tree(walk->child, level + 1, 1, 0, !walk->next,
                      current->uid, 0);
          }
        }
        // 403d2c
        return;
    }
    // 40418c
    width[level] = comm_len + cur_x - offset + add;
    // 4044c4
    if (cur_x >= output_width && trunc) {
        out_string(sym->first_3);
        out_string("+");
        // 404508
        out_newline();
        return;
    }
    // 4041c0
    first = 1;  // [sp, #96]
    for (walk = current->children; walk; walk = next) {
        count = 0;  // w23
        next = walk->next;
        // 4041dc   // 404254 循环开始之后从这里
        if (compact) {
            // 404258
            scan = &walk->next;
            // 40425c
            while (*scan)   // *scan = walk->next
                // 404268
                if (!tree_equal(walk->child, (*scan)->child))   // 4034a0
                    // 404280
                    scan = &(*scan)->next;
                // 4043d8
                else {
                    if (next == *scan)
                        // 4043e0
                        next = (*scan)->next;
                    count++;
                    tmp_child = (*scan)->next;
                    free(*scan);
                    *scan = tmp_child;
                }
        }
        // 4041e0
        if (first) {
            // 4041e8
            out_string(next ? sym->first_3 : sym->single_3);
            first = 0;
        }
        // 40420c
        dump_tree(walk->child, level + 1, count + 1,
                  walk == current->children, !next, current->uid,
                  closing + (count ? 1 : 0));
    }
}

static int tree_equal(const PROC * a, const PROC * b)
{
    const CHILD *walk_a, *walk_b;
    int i;

    // 4034b4
    if (strcmp(a->comm, b->comm))
        return 0;
    // 4034bc
    if (user_change && a->uid != b->uid)
        return 0;
    // 4034cc
    if (ns_change) {
        for (i = 0; i < NUM_NS; i++)
            if (a->ns[i] != b->ns[i])
                return 0;
    }
    // 4034f4
    for (walk_a = a->children, walk_b = b->children; walk_a && walk_b;
         walk_a = walk_a->next, walk_b = walk_b->next)
        // 403508
        if (!tree_equal(walk_a->child, walk_b->child))
            return 0;
    return !(walk_a || walk_b);
}

static void dump_by_user(PROC * current, uid_t uid)
{
    const CHILD *walk;

    // 404670
    if (!current)
        return;
    // 40468c
    if (current->uid == uid) {
        // 4046c0
        if (dumped)
            putchar('\n');
        dump_tree(current, 0, 1, 1, 1, uid, 0);
        dumped = 1;
        return;
    }
    // 404694
    for (walk = current->children; walk; walk = walk->next)
        dump_by_user(walk->child, uid);
}

static void out_char(char c)
{
    // 403370
    if (charlen == 0) {                /* "new" character */
        // 4033c8 -> 40338c
        if ((c & 0x80) == 0) {
            charlen = 1;        /* ASCII */
        // 4033e8
        } else if ((c & 0xe0) == 0xc0) {        /* 110.. 2 bytes */
            charlen = 2;
        // 4033f8
        } else if ((c & 0xf0) == 0xe0) {        /* 1110.. 3 bytes */
            charlen = 3;
        // 403408
        } else if ((c & 0xf8) == 0xf0) {        /* 11110.. 4 bytes */
            charlen = 4;
        } else {
            charlen = 1;
        }
        cur_x++;                /* count first byte of whatever it is only */
    }
    // 403384 || 40338c
    charlen--;          // 汇编代码有点难理解。
    // 403394
    if (!trunc || cur_x <= output_width)
        putchar(c);
    // 4033ac
    else {
        if (trunc && (cur_x == output_width + 1))
            putchar('+');
    }
}

static void out_string(const char *str)
{
    while (*str)
        out_char(*str++);
}

static void dump_by_namespace(struct ns_entry *root)
{
    struct ns_entry *ptr = root;
    CHILD *c;
    char buff[14];

    // 402670
    for ( ; ptr; ptr = ptr->next) {
        snprintf(buff, sizeof(buff), "[%li]\n", (long int)ptr->number);
        // 4026a0
        out_string(buff);
        // 4026bc
        for (c = ptr->children; c; c = c->next)
            dump_tree(c->child, 0, 1, 1, 1, 0, 0);
    }
}

static void free_children(CHILD *children)
{
    CHILD *walk, *next;

    // 402734
    walk = children;
    while (walk != NULL) {
        next = walk->next;
        free(walk);
        walk = next;
    }
}

static void free_buffers()
{
    // 4026f8
    if (width != NULL) {
        free(width);
        width = NULL;
    }
    // 402708
    if (more != NULL) {
        free(more);
        more = NULL;
    }
    // 402718
    capacity = 0;
    // 402720
}

static void free_proc()
{
    // 402720
    PROC *walk, *next;
    walk = list;
    // 402734
    while (walk != NULL) {
        next = walk->next;
        free_children(walk->children);
        // 402748
        if (walk->argv) {
            free(walk->argv[0]);
            free(walk->argv);
        }
        // 402760
        free(walk);
        walk = next;
    }
    // 40276c
    list = NULL;
}

static void free_namespace(struct ns_entry **nsroot)
{
    // 402774
    struct ns_entry *walk, *next;
    walk = *nsroot;
    // 402784
    while (walk != NULL) {
        next = walk->next;
        free_children(walk->children);
        free(walk);
        walk = next;
    }
    // 4027a4
    *nsroot = NULL;
}

static void find_ns_and_add(struct ns_entry **root, PROC *r, enum ns_type id)
{
    struct ns_entry *ptr, *last = NULL;
    CHILD *tmp_child, **c;

    // 4031f4
    for (ptr = *root; ptr; ptr = ptr->next) {
        // 40321c
        if (ptr->number == r->ns[id])
            break;
        last = ptr;
    }

    if (!ptr) {
        // 4032fc || 403320
        if (!(ptr = malloc(sizeof(*ptr)))) {
            perror("malloc");
            exit(1);
        }

        // 403308 || 40332c
        memset(ptr, 0, sizeof(*ptr));
        // 403310 || 40333c
        ptr->number = r->ns[id];
        if (*root == NULL)
            // 403340
            *root = ptr;
        else
            // 403314
            last->next = ptr;
    }

    /* move the child to under the namespace's umbrella */
    // 403224
    for (c = &ptr->children; *c; c = &(*c)->next)
        ;

    // 40323c
    if (!(*c = malloc(sizeof(CHILD)))) {
        perror("malloc");
        exit(1);
    }

    // 403254
    (*c)->child = r;
    (*c)->next = NULL;

    /* detaching from parent */
    // 403258
    if (r->parent) {
        // 40325c
        for (c = &r->parent->children; *c; c = &(*c)->next) {
            if ((*c)->child == r) {
                tmp_child = (*c)->next;
                // 403284
                free(*c);
                *c = tmp_child;
                break;
            }
        }
        // 403290
        r->parent = NULL;
    }

}

static void sort_by_namespace(PROC *r, enum ns_type id, struct ns_entry **root)
{
    CHILD *walk, *next;

    /* first run, find the first process */
    // 4031d0
    if (!r) {
        // 4032cc
        r = find_proc(1);
        if (!r)
            return;
    }
    
    // 4031d4
    if (r->parent == NULL || r->parent->ns[id] != r->ns[id])
        // 4031f4
        find_ns_and_add(root, r, id);

    walk = r->children;
    while (walk) {
        next = walk->next;
        sort_by_namespace(walk->child, id, root);
        walk = next;
    }
}

static void trim_tree_by_parent(PROC * current)
{
  PROC * parent = current->parent;

  // 402af8
  if (!parent) {
    // cbz 402b74   跳出递归，顺序执行  -> b 402658
    return;
  }

  free_children(parent->children);
  // 402b18
  parent->children = NULL;
  // 402b1c
  add_child(parent, current);
  trim_tree_by_parent(parent);
}

static double uptime()
{
    // 402898
    char * savelocale;
    char buf[2048];
    FILE* file;

    // 4028c0
    if (!(file = fopen("/proc/uptime", "r"))) {
        // cbz 402e24
        printf("error opening uptime file.\n");
        exit(1);
    }

    // 4028d8
    savelocale = setlocale(LC_NUMERIC,"C");
    // 4028f8
    if (fscanf(file, "%2047s", buf) == EOF) perror("uptime");
    fclose(file);
    // 402928
    setlocale(LC_NUMERIC,savelocale);
    return atof(buf);
}

static double process_age(const unsigned long long jf)
{
    // 402880
    double age;
    double sc_clk_tck = sysconf(_SC_CLK_TCK);
    // 402898
    assert(sc_clk_tck > 0);
    // 4028c0
    age = uptime() - jf / sc_clk_tck;
    if (age < 0L)
        return 0L;
    return age;
}

static char* get_threadname(const pid_t pid, const int tid, const char *comm)
{
    FILE *file;
    char *thread_comm, *endcomm, *threadname;
    char *path = NULL;
    int len, nbytes;
    char readbuf[BUFSIZ + 1];
    
    if ((threadname = malloc(0x43) == NULL) {
        // cbz 402e90
        exit(2);
    }

    if (thread_names == 0) {
        // cbz 402dac
        sprintf(threadname, THREAD_FORMAT, 0x40, comm);
        return threadname;
        // b 402d1c
    }

    len = snprintf(NULL, 0, "%s/%d/task/%d/stat", PROC_BASE, pid, tid);
    len++;

    if ((path = malloc(len) == NULL) {
        // cbz 402e90
        exit(2);
    }

    nbytes = snprintf(path, len, "%s/%d/task/%d/stat", "/proc", pid, tid);
    if (nbytes < 0 || nbytes >= len)
        perror("get_threadname: snprintf");
    
    if ((file = fopen(path, "r") != NULL) {
        if (fgets(readbuf, BUFSIZ, file) != NULL) {
            if ((thread_comm = strchr(readbuf, '('))
                && (endcomm = strrchr(thread_comm, ')'))) {
                // cbnz 402df8
                ++thread_comm;
                *endcomm = '\0';
                sprintf(threadname, THREAD_FORMAT, COMM_LEN, thread_comm);
                (void) fclose(file);
                free(path);
                return threadname;
                // 402d1c
            }
        }   // if (fgets(readbuf, BUFSIZ, file) != NULL)

        fclose(file);
    }   // if ((file = fopen(path, "r") != NULL)

    free(path);
    sprintf(threadname, THREAD_FORMAT, COMM_LEN, comm);
    return threadname;
    // 402d1c
}

static void read_proc(void)
{
  DIR *dir;
  struct dirent *de;
  FILE *file;
  struct stat st;
  char *path, *comm;
  char *buffer;
  size_t buffer_size;
  char readbuf[BUFSIZ + 1];
  char *tmpptr, *endptr;
  pid_t pid, ppid, pgid;
  int fd, size;
  int empty;
  unsigned long long proc_stt_jf = 0;
  double process_age_sec = 0;

    if (trunc == 0) {
        // cbz 402818
        buffer_size = 0x2001;
    } else {
        buffer_size = output_width + 1;
    }

    if (!print_args) {
        // cbz 402810 --> 402108
        buffer = NULL;
    }
    else if (!(buffer = malloc(buffer_size))) {
        // cbz 402e60
        perror("malloc");
        exit(1);
    }

    if (!(dir = opendir("/proc"))) {
        // cbz 402e48
        perror("/proc");
        exit(1);
    }

    // 402130
    while ((de = readdir(dir)) != NULL) {
        pid = (pid_t) strtol(de->d_name, &endptr, 0xa);
        if (endptr != de->d_name && endptr[0] == '\0') {
            if (! (path = malloc(strlen(de->d_name) + 0xf)))
                exit(2);
            sprintf(path, "%s/%d/stat", "/proc", pid);
            if ((file = fopen(path, "r")) != NULL) {
                sprintf(path, "%s/%d", "/proc", pid);
                if (stat(path, &st) < 0) {
                    // tbnz 4025a8
                    fclose(file);
                    free(path);
                    continue;
                }

                size = fread(readbuf, 1, 0x2000, file);
                if (ferror(file) == 0) {
                    readbuf[size] = 0;
                    if ((comm = strchr(readbuf, '(')) && 
                        (tmpptr = strrchr(comm, ')'))) {
                        *tmpptr = 0;
                        // x0 = tmpptr + 2
                        if (sscanf(tmpptr + 2, 
                            "%*c %d %d %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %Lu",
                            &ppid, &pgid, &proc_stt_jf) == 3) {
                            // b.eq 402880
                            DIR *taskdir;
                            struct dirent *dt;
                            char *taskpath;
                            int thread;

                            // 从 402880 处开始到此处，抽出来写成函数后为以下码
                            process_age_sec = process_age(proc_stt_jf);

                            ++comm;

                            // 顺序执行到 402954
                            if (hide_threads == 0) {
                                // cbz 402a74
                                if (!(taskpath = malloc(strlen(path) + 0xa))) {
                                    // cbz 402e90
                                    exit(2);
                                }
                                sprintf(taskpath, "%s/task", path);
                                if (taskdir = opendir(taskpath)) != 0) {
                                    // cbna 402be8
                                    while ((dt = readdir(taskdir)) != NULL) {
                                        if (thread != pid) {
                                            // 402c34
                                            char *threadname;
                                            threadname = get_threadname(pid, thread, comm);

                                            // 402d1c
                                            if (print_args == 0) {
                                                // cbz 402d84
                                                add_proc(threadname, thread, pid, pgid, st.st_uid,
                                                                  NULL, 0, 1, process_age_sec);
                                            }
                                            else {
                                                add_proc(threadname, thread, pid, pgid, st.st_uid,
                                                                  threadname, strlen (threadname) + 1, 1, process_age_sec);
                                            }
                                            free(threadname);
                                        }   // if (thread != pid)
                                    }   // while ((dt = readdir(taskdir)) != NULL)
                                    // 402d6c
                                    closedir(taskdir);
                                }
                                free(taskpath);
                            }
                            
                            // 402964
                            if (print_args == 0) {
                                // cbz 402a44
                                add_proc(comm, pid, ppid, pgid, st.st_uid, NULL, 0, 0,
			                            process_age_sec);
                                // b 40224c
                            }
                            else {                            
                                sprintf(path, "%s/%d/cmdline", PROC_BASE, pid);
                                if ((fd = open(path, O_RDONLY)) < 0) {
                                    // tbnz 402b84
                                    fclose(file);
                                    free(path);
                                    continue;
                                    // b 402130
                                }

                                if ((size = read(fd, buffer, buffer_size)) < 0) {
                                    // tbnz 402b80
                                    fclose(fd);
                                    fclose(file);
                                    free(path);
                                    continue;
                                    // b 402130
                                }
                                close(fd);
                                if (size >= buffer_size)
                                    size--;
                                if (size) {
                                    buffer[size++] = 0;
                                }

                                add_proc(comm, pid, ppid, pgid, st.st_uid,
                                    buffer, size, 0, process_age_sec);

                                // b 40224c
                            }
                        }   // if (sscanf
                    }   // if ((comm = strchr(readbuf, '(')) && (tmpptr = strrchr(comm, ')')))
                }   // if (ferror(file) == 0)
                // 40224c
                fclose(file);
            }   // if ((file = fopen(path, "r")) != NULL) 
            free(path);
        }   // if (endptr != de->d_name && endptr[0] == '\0') {
    }   // end while (readdir(dir) != NULL)

    // 40226c
    closed(dir);

    fix_orphans();

    // 4025f4
    if (print_args) {
        // cbnz 4027e0
        free(buffer)
        // b 4025fc
    }

    // 4025fc
    if (empty) {
        // cbnz 402e98
        printf("%s is empty (not mounted ?)\n", "/proc");
        exit(1);
    }
}

static PROC *find_proc(pid_t pid)
{
    PROC *walk;

    for (walk = list; walk; walk = walk->next) {
        // b.ne	402284
        if (walk->pid == pid)
            return walk;        // b 4025d8
    }
    return NULL;    // cbz 4027ec
}

static void fix_orphans(void)
{
    PROC *root, *walk;

    // 402274
    if (!(root = find_proc(ROOT_PID))) {
        // 4027ec
        root = new_proc("?", ROOT_PID, 0);
    }
    // (4027ec) -> cbnz	4025d8 or -> b 4025f4
    // (4025d8) -> order 4025f4
    for (walk = list; walk; walk = walk->next) {
        if (walk->pid == 1 || walk->pid == 0) {
            // b.ls 4025d0
            continue;
        }
        if (walk->parent == NULL) {
            add_child(root, walk);
            walk->parent = root;
        }
    }
    // 4025f4
}

int main(int argc, char **argv)
{
    struct option options[] = {
        {"arguments", 0, NULL, 'a'},
        {"ascii", 0, NULL, 'A'},
        {"compact-not", 0, NULL, 'c'},
        {"color", 1, NULL, 'C'},
        {"vt100", 0, NULL, 'G'},
        {"highlight-all", 0, NULL, 'h'},
        {"highlight-pid", 1, NULL, 'H'},
        {"long", 0, NULL, 'l'},
        {"numeric-sort", 0, NULL, 'n'},
        {"ns-sort", 1, NULL, 'N' },
        {"show-pids", 0, NULL, 'p'},
        {"show-pgids", 0, NULL, 'g'},
        {"show-parents", 0, NULL, 's'},
        {"ns-changes", 0, NULL, 'S' },
        {"thread-names", 0, NULL, 't'},
        {"hide-threads", 0, NULL, 'T'},
        {"uid-changes", 0, NULL, 'u'},
        {"unicode", 0, NULL, 'U'},
        {"version", 0, NULL, 'V'},
        {"security-context", 0, NULL, 'Z'},
        { 0, 0, 0, 0 }
    };

    char *ep, *env_columns;
    struct winsize winsz;

    env_columns = getenv("COLUMNS");
    if (env_columns && *env_columns) {
        // b 4024e0
        long t;
        t = strtol(env_columns, &ep, 0);
        if (!*ep && (t > 0) && (t < 0x7fffffffL)) {   // 不成立时 --> 401f78
            // w0 = t;  --> b 401f9c
            // return (int)t;
            output_width = t;
            goto ac_401f9c;
        }
    }
    output_width = 0x84;
    if (ioctl(1, TIOCGWINSZ, &winsz) >= 0)             
        if (winsz.ws_col)
            output_width = winsz.ws_col;

    ac_401f9c:
    if (!strcmp(__progname, "pstree.x11"))
        wait_end = 1;

    if (isatty(1) && !strcmp(nl_langinfo(0xe), "UTF-8")) {
        // b 4024d4 -> 402004
        sym = &sym_utf;
    }
    else if (isatty(1) && (termname = getenv("TERM")) &&
               (strlen(termname) > 0) &&
               (setupterm(NULL, 1 /* stdout */ , NULL) == OK) &&
               (tigetstr("acsc") != NULL) && (tigetstr("acsc") != (char *)-1)) {
        // b 402510 --> b 401ffc，因代码相同，所以跳转到 401ffc
        sym = &sym_ascii;
    }
    else {
        sym = &sym_ascii;
    }

    // 402004
    highlight = 0;

    while ((c = getopt_long(argc, argv, "aAcC:GhH:nN:pglsStTuUVZ", options, NULL)) != -1) {
        switch (c) {
            default:
                usage();
            case 'p':
                pids = 1;

            case 'c':
                compact = 0;
                break;

            case 'a':
                print_args = 1;
                break;
            
            case 'Z':
                show_scontext = 1;
                break;
            
            case 'V':
                print_version();
                return 0;

            case 'U':
                sym = &sym_utf;
                break;

            case 'T':
                hide_threads = 1;
                break;
            
            case 'S':
                ns_change = 1;
                break;

            case 'N':
                nsid = get_ns_id(optarg);
                if (nsid == NUM_NS)
                     usage();
                if (verify_ns(nsid)) {
                     fprintf(stderr,
                             _("procfs file for %s namespace not available\n"),
                             optarg);
                     return 1;
                }
                break;

            case 'H':
                if (highlight)
                    usage();
                if (!getenv("TERM")) {
                    fprintf(stderr, _("TERM is not set\n"));
                    return 1;
                }
                if (tgetent(termcap_area, getenv("TERM")) <= 0) {
                    fprintf(stderr, _("Can't get terminal capabilities\n"));
                    return 1;
                }
                if (!(highlight = atoi(optarg)))
                    usage();
                break;

            case 'G':
                sym = &sym_vt100;
                break;

            case 'C':
                if (strcasecmp("age", optarg) == 0) {
                    color_highlight = COLOR_AGE;
                } else {
                    usage();
                }
                break;

            case 'A':
                sym = &sym_ascii;
                break;

            case 'u':
                user_change = 1;
                break;

            case 't':
                thread_names = 1;
                break;

            case 's':
                show_parents = 1;
                break;

            case 'n':
                by_pid = 1;
                break;

            case 'l':
                trunc = 0;
                break;

            case 'h':
                if (highlight)
                    usage();
                if (getenv("TERM") && tgetent(termcap_area, getenv("TERM")) > 0)
                    highlight = getpid();
                break;

            case 'g':
                pgids = 1;
                break;
        }   // switch (c)
    }

    pw = NULL;
    pid = 0x1;
    pid_set = 0;

    if (optind == argc - 1) {
        // b.eq 40255c
        if (!isdigit(*argv[optind])) {
            // tbz 402a08
            if (!(pw = getpwnam(argv[optind++]))) {
                // cbz 402dc0
                fprintf(stderr, _("No such user name: %s\n"),
                        argv[optind - 1]);
                return 1;
            }
            pid_set = 0;
            pid = 1;
            // b 4020c0
        }
        else if (isdigit(*argv[optind])) {
            pid = (pid_t) strtol(argv[optind++], &endptr, 10);
            if (endptr[0] != '\0') {
                // cbnz 402054
                usage();
            }
            pid_set = 1;
            // b 4020c0
        }
    }

    if (optind != argc) {
        // b.ne 402054
        usage();
    }

    read_proc();
    
    // 此处if为多余代码
    if (list == NULL) {
        // cbn 402b50
    }
    else {
        // cbz x0, 402654
        for (current = find_proc(highlight); current; current = current->parent) {
            // cbnz 402640
            current->flags |= PFLAG_HILIGHT;
        }
    }
    // order 402654
    
    // cbn 402b50   # 此处为多余代码
    #if 0
    if (show_parents & pid_set == 0) {
        // cbz 402658
    }
    #endif
    // 402654
    if (show_parents & pid_set != 0) {
        // cbnz 402ad8
        PROC *child_proc;
        if ( (child_proc = find_proc(pid)) == NULL) {
            // cbz 402b54
            fprintf(stderr, _("Process %d not found.\n"), pid);
            return 1;
        }

        // 402af4
        trim_tree_by_parent(child_proc);
        // 402b74
        pid = 1;
        // b 402658
    }
    // order 402658    

    // 402658
    if (nsid == 0x8) {
        // b.eq 402824
        dump_by_user(find_proc(ROOT_PID), pw->pw_uid);  // 4026f8 -> free_buffers()
        if (!dumped) {
            fprintf(stderr, _("No processes found.\n"));
            return 1;
        }
    }
    else if (nsid != 0x8 {
        // 402660
        sort_by_namespace(NULL, nsid, &nsroot);
        dump_by_namespace(nsroot);
        // order 4026f8
    }
    // 402824
    else if (!pw) {
        // 402a28 -> 402b30
        dump_tree(find_proc(pid), 0, 1, 1, 1, 0, 0);
        // 4026f8
    }

    // 4026f8
    free_buffers();
    // 402720
    free_proc();
    // 402774
    free_namespace(&nsroot);
    // 4027ac
    if (wait_end == 1) {
        fprintf(stderr, _("Press return to close\n"));
        (void) getchar();
    }

    return 0;
}
